﻿using CodeAngel.Domain;
using CodeAngel.Domain.Aggregates;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;


namespace CodeAngel.UI
{
    public class MainWindowViewModel : ViewModel
    {
        #region Constructors

        /// <summary>
        /// Constructor for this view model.  By default, loads the CodeAngel v9 Domain.
        /// </summary>
        public MainWindowViewModel()
        {
            this.LogInUserAsync();
        }

        #endregion

        #region CurrentDomain

        /// <summary>
        /// Gets or sets the CurrentDomain.
        /// </summary>
        public Domain.Domain CurrentDomain
        {
            get { return this._currentDomain; }
            private set
            {
                if (value != this._currentDomain)
                {
                    this._currentDomain = value;
                    this.RaisePropertyChanged("CurrentDomain");
                    this.UpdatedEntityDefsCollectionView();
                    this.InitializeDevelopmentDatabaseViewModel();
                    this.InitializeProductionDatabaseViewModel();
                }
            }
        }
        private Domain.Domain _currentDomain;

        #region LoadDomainAsync

        /// <summary>
        /// Loads a full editable view of the identified domain, and makes it available through the 
        /// CurrentDomain property.
        /// </summary>
        private async void LoadDomainAsync(Guid domainID)
        {
            this.CurrentDomain = null;
            this._currentDomainAggregate = new DomainFullAggregate();
            await this._currentDomainAggregate.LoadAsync(domainID);
            this.CurrentDomain = this._currentDomainAggregate.Root;
            this.UpdateDistinctSubFolders();
        }
        private DomainFullAggregate _currentDomainAggregate;


        private void LoadDefaultDomain()
        {
            //Guid DomainID = new Guid("F28AD511-70D3-4758-B8B9-767871D7E32F"); // AI University
            Guid DomainID = new Guid("8b2c0a8e-9acb-46c7-ab5d-732bc6bb1a41"); // Treever
            this.LoadDomainAsync(DomainID);
        }

        #endregion

        #region OpenDomain

        /// <summary>
        /// Prompts the user to open a Domain from a list of available Domains.  If the
        /// user selects a Domain to open, it will be loaded in an editable aggregate, and
        /// set at the CurrentDomain.
        /// </summary>
        public void OpenDomain()
        {
            var ViewModel = new SelectDomainViewModel();
            var Window = new SelectDomainWindow();
            Window.Owner = App.Current.MainWindow;
            Window.DataContext = ViewModel;
            ViewModel.DomainSelected += delegate 
            {
                if (ViewModel.SelectedDomain != null)
                  this.LoadDomainAsync(ViewModel.SelectedDomain.ID);
                Window.Close();
            };

            Window.Show();

        }

        /// <summary>
        /// Delegate command that can execute OpenDomain.
        /// </summary>
        public DelegateCommand OpenDomainCommand
        {
            get
            {
                if (this._OpenDomainCommand == null) this._OpenDomainCommand = new DelegateCommand(OpenDomain);
                return _OpenDomainCommand;
            }
        }
        private DelegateCommand _OpenDomainCommand;

        #endregion

        #region CreateNewDomain

        /// <summary>
        /// Prompts the user with a dialog for creating a new domain.  If the user supplies
        /// the required data and selects to create a new domain, a new domain will be created
        /// and the CurrentDomain property will be set to the new domain.
        /// </summary>
        public void CreateNewDomain()
        {
            var NewDomainWindow = new NewDomainWindow();
            NewDomainWindow.Owner = App.Current.MainWindow;
            if (NewDomainWindow.ShowDialog() == true)
            {
                this._currentDomainAggregate = new DomainFullAggregate();
                this._currentDomainAggregate.CreateNew();
                this.CurrentDomain = this._currentDomainAggregate.Root;
                this.CurrentDomain.Name = NewDomainWindow.Name;
                this.CurrentDomain.Namespace = this.CurrentDomain.Name;
            }
        }

        /// <summary>
        /// Delegate command that can execute CreateNewDomain.
        /// </summary>
        public DelegateCommand CreateNewDomainCommand
        {
            get
            {
                if (this._CreateNewDomainCommand == null) this._CreateNewDomainCommand = new DelegateCommand(CreateNewDomain);
                return _CreateNewDomainCommand;
            }
        }
        private DelegateCommand _CreateNewDomainCommand;

        #endregion

        #region SaveCurrentDomain

        /// <summary>
        /// Saves any changes made to the CurrentDomain.
        /// </summary>
        public async void SaveCurrentDomain()
        {
            //var x = this.CurrentDomain.GetInvalidRules();
            
            if (this._currentDomainAggregate == null) return;
            try
            {
                await this._currentDomainAggregate.SaveAsync();
                this.CurrentDomain = this._currentDomainAggregate.Root;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Unexpected error trying to save", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Delegate command that can execute SaveCurrentDomain.
        /// </summary>
        public DelegateCommand SaveCurrentDomainCommand
        {
            get
            {
                if (this._SaveCurrentDomainCommand == null) this._SaveCurrentDomainCommand = new DelegateCommand(SaveCurrentDomain);
                return _SaveCurrentDomainCommand;
            }
        }
        private DelegateCommand _SaveCurrentDomainCommand;

        #endregion

        #region GenerateDomainFiles

        /// <summary>
        /// Generates the files for the current Domain.
        /// </summary>
        public void GenerateDomainFiles()
        {
            Task.WaitAll(this.CurrentDomain.CreateOrUpdateFilesAsync());
            MessageBox.Show("Files successfully generated");

            //this.CurrentDomain.CreateDtoExtensionsFileAsync();
            //this.CurrentDomain.CreateDtoHostFileAsync();
            //this.CurrentDomain.CreateAggregateExtensionsFileAsync();

            //foreach (var ThisED in this.CurrentDomain.EntityDefs)
            //{
            //    foreach (var ThisAD in ThisED.AggregateDefs)
            //        ThisAD.CreateAggregateFileAsync();

            //    var Tasks = new Task[] 
            //    { 
            //        ThisED.CreateDtoFileAsync(),
            //        ThisED.CreateEntityFilesAsync(),
            //        ThisED.CreateEntityCollectionFilesAsync(),
            //        ThisED.CreateFilterFilesAsync(),
            //        ThisED.CreateCriteriaFileAsync(),
            //        ThisED.CreateDeleteAggregateFileAsync()
            //    };

            //    Task.WaitAll(Tasks);
            //}

            

        }


        /// <summary>
        /// Delegate command that can execute GenerateDomainFiles.
        /// </summary>
        public DelegateCommand GenerateDomainFilesCommand
        {
            get
            {
                if (this._GenerateDomainFilesCommand == null) this._GenerateDomainFilesCommand = new DelegateCommand(GenerateDomainFiles);
                return _GenerateDomainFilesCommand;
            }
        }
        private DelegateCommand _GenerateDomainFilesCommand;

        #endregion

        #region GenerateDomainFilesAndClose

        /// <summary>
        /// Generates the domain files for the current domain then closes this application.
        /// </summary>
        public void GenerateDomainFilesAndClose()
        {
            Task.WaitAll(this.CurrentDomain.CreateOrUpdateFilesAsync());
            App.Current.MainWindow.Close();
        }

        /// <summary>
        /// Delegate command that can execute GenerateDomainFilesAndClose.
        /// </summary>
        public DelegateCommand GenerateDomainFilesAndCloseCommand
        {
            get
            {
                if (this._GenerateDomainFilesAndCloseCommand == null) this._GenerateDomainFilesAndCloseCommand = new DelegateCommand(GenerateDomainFilesAndClose);
                return _GenerateDomainFilesAndCloseCommand;
            }
        }
        private DelegateCommand _GenerateDomainFilesAndCloseCommand;

        #endregion

        #endregion

        #region DevelopmentDatabaseViewModel

        /// <summary>
        /// Gets or sets the DevelopmentDatabaseViewModel.
        /// </summary>
        public DatabaseViewModel DevelopmentDatabaseViewModel
        {
            get { return this._DevelopmentDatabaseViewModel; }
            set
            {
                if (value != this._DevelopmentDatabaseViewModel)
                {
                    this._DevelopmentDatabaseViewModel = value;
                    this.RaisePropertyChanged("DevelopmentDatabaseViewModel");
                }
            }
        }
        private DatabaseViewModel _DevelopmentDatabaseViewModel;


        private void InitializeDevelopmentDatabaseViewModel()
        {
            if (this.CurrentDomain == null)
                this.DevelopmentDatabaseViewModel = null;
            else
            {
                if (this.CurrentDomain.DevelopmentDatabase == null) this.CurrentDomain.CreateDevelopmentDatabase();
                this.DevelopmentDatabaseViewModel = new DatabaseViewModel() { Database = this.CurrentDomain.DevelopmentDatabase };
            }
        }

        #endregion

        #region ProductionDatabaseViewModel

        /// <summary>
        /// Gets or sets the ProductionDatabaseViewModel.
        /// </summary>
        public DatabaseViewModel ProductionDatabaseViewModel
        {
            get { return this._ProductionDatabaseViewModel; }
            set
            {
                if (value != this._ProductionDatabaseViewModel)
                {
                    this._ProductionDatabaseViewModel = value;
                    this.RaisePropertyChanged("ProductionDatabaseViewModel");
                }
            }
        }
        private DatabaseViewModel _ProductionDatabaseViewModel;


        private void InitializeProductionDatabaseViewModel()
        {
            if (this.CurrentDomain == null)
                this.ProductionDatabaseViewModel = null;
            else
            {
                if (this.CurrentDomain.ProductionDatabase == null) this.CurrentDomain.CreateProductionDatabase();
                this.ProductionDatabaseViewModel = new DatabaseViewModel() { Database = this.CurrentDomain.ProductionDatabase };
            }
        }

        #endregion

        #region EntityDefs

        #region EntityDefsCollectionView

        /// <summary>
        /// Gets or sets the EntityDefsCollectionView.
        /// </summary>
        public ListCollectionView EntityDefsCollectionView
        {
            get { return this._entityDefsCollectionView; }
            set
            {
                if (value != this._entityDefsCollectionView)
                {
                    this._entityDefsCollectionView = value;
                    this.RaisePropertyChanged("EntityDefsCollectionView");
                }
            }
        }
        private ListCollectionView _entityDefsCollectionView;

        /// <summary>
        /// Updates the EntityDefsCollectionView property with the entity defs defined in the
        /// current domain.
        /// </summary>
        private void UpdatedEntityDefsCollectionView()
        {
            if (this.CurrentDomain != null)
            {
                this._entityDefs = new List<EntityDef>(this.CurrentDomain.EntityDefs);
                this.EntityDefsCollectionView = new ListCollectionView(this._entityDefs);
                this.EntityDefsCollectionView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
                //BindingOperations.EnableCollectionSynchronization(this.EntityDefsCollectionView, _entityDefsLock);
            }
            else
            {
                this._entityDefs = null;
                this.EntityDefsCollectionView = null;
            }
        }
        private List<EntityDef> _entityDefs;
        //private object _entityDefsLock = new object();

        #endregion

        #region CreateNewEntityDef

        /// <summary>
        /// Prompts the user to create a new EntityDef object.
        /// </summary>
        public void CreateNewEntityDef()
        {
            if (this.CurrentDomain == null) return;
            var NewEntityDef = this.CurrentDomain.EntityDefs.CreateNew();
            var ViewModel = new EntityDefViewModel(NewEntityDef);
            var Window = new EntityDefWindow();
            Window.DataContext = ViewModel;
            Window.Owner = App.Current.MainWindow;
            if (Window.ShowDialog() == true)
            {
                this.CurrentDomain.EntityDefs.Add(NewEntityDef);
                this.EntityDefsCollectionView.AddNewItem(NewEntityDef);
                this.EntityDefsCollectionView.CommitNew();
                if (!this.DistinctSubFolders.Contains(NewEntityDef.SubFolder))
                    this.DistinctSubFolders.Add(NewEntityDef.SubFolder);
            }
            else
            {
                this.CurrentDomain.EntityDefs.Remove(NewEntityDef);
            }
        }

        /// <summary>
        /// Delegate command that can execute CreateNewEntityDef.
        /// </summary>
        public DelegateCommand CreateNewEntityDefCommand
        {
            get
            {
                if (this._createNewEntityDefCommand == null) this._createNewEntityDefCommand = new DelegateCommand(CreateNewEntityDef);
                return _createNewEntityDefCommand;
            }
        }
        private DelegateCommand _createNewEntityDefCommand;

        #endregion

        #region OpenEntityDef

        public void OpenEntityDef(EntityDef thisED)
        {
            var ViewModel = new EntityDefViewModel(thisED);
            var Window = new EntityDefWindow();
            Window.DataContext = ViewModel;
            Window.Owner = App.Current.MainWindow;
            Window.ShowDialog();
            if (!thisED.IsValid())
                if (MessageBox.Show(thisED.GetInvalidRules().ToString(), "Entity not valid", MessageBoxButton.OKCancel) == MessageBoxResult.Cancel)
                    this.OpenEntityDef(thisED);
        }

        /// <summary>
        /// Delegate command that can execute OpenEntityDef.
        /// </summary>
        public DelegateCommand<EntityDef> OpenEntityDefCommand
        {
            get
            {
                if (this._OpenEntityDefCommand == null) this._OpenEntityDefCommand = new DelegateCommand<EntityDef>(OpenEntityDef);
                return this._OpenEntityDefCommand;
            }
        }
        private DelegateCommand<EntityDef> _OpenEntityDefCommand;

        #endregion

        #region DeleteEntityDef

        /// <summary>
        /// Flags the identified EntityDef as deleted.  Won't actually be deleted until the 
        /// next save operation on the respective aggregate.
        /// </summary>
        public void DeleteEntityDef(EntityDef thisED)
        {
            thisED.Delete();
        }

        /// <summary>
        /// Delegate command that can execute DeleteEntityDef.
        /// </summary>
        public DelegateCommand<EntityDef> DeleteEntityDefCommand
        {
            get
            {
                if (this._deleteEntityDefCommand == null) this._deleteEntityDefCommand = new DelegateCommand<EntityDef>(DeleteEntityDef);
                return this._deleteEntityDefCommand;
            }
        }
        private DelegateCommand<EntityDef> _deleteEntityDefCommand;

        #endregion

        #region Filter

        #region DistinctSubFolders

        /// <summary>
        /// Gets or sets the DistinctSubFolders.
        /// </summary>
        public ObservableCollection<string> DistinctSubFolders
        {
            get { return this._DistinctSubFolders; }
            set
            {
                if (value != this._DistinctSubFolders)
                {
                    this._DistinctSubFolders = value;
                    this.RaisePropertyChanged("DistinctSubFolders");
                }
            }
        }
        private ObservableCollection<string> _DistinctSubFolders = new ObservableCollection<string>();

        private void UpdateDistinctSubFolders()
        {
            var SubFolders = new ObservableCollection<string>();
            SubFolders.Add(string.Empty);
            if (this.CurrentDomain != null)
            {
                foreach (var ThisED in this.CurrentDomain.EntityDefs)
                    if (!SubFolders.Contains(ThisED.SubFolder))
                        SubFolders.Add(ThisED.SubFolder);
            }
            this.DistinctSubFolders = SubFolders;
        }

        #endregion

        #region FilterEntityDefsByThisSubFolder

        /// <summary>
        /// Gets or sets the FilterEntityDefsByThisSubFolder.
        /// </summary>
        public string FilterEntityDefsByThisSubFolder
        {
            get { return this._filterEntityDefsByThisSubFolder; }
            set
            {
                if (value != this._filterEntityDefsByThisSubFolder)
                {
                    this._filterEntityDefsByThisSubFolder = value;
                    this.RaisePropertyChanged("FilterEntityDefsByThisSubFolder");
                    this.FilterEntityDefs();
                }
            }
        }
        private string _filterEntityDefsByThisSubFolder = string.Empty;

        private void FilterEntityDefs()
        {
            this.EntityDefsCollectionView.Filter = new Predicate<object>(EntityDefMeetsFilter);
        }

        private bool EntityDefMeetsFilter(object thisED)
        {
            if (this.FilterEntityDefsByThisSubFolder.Length > 0)
                return (thisED as EntityDef).SubFolder == this.FilterEntityDefsByThisSubFolder;
            else
                return true;
        }


        #endregion

        #region ClearFilterEntityDefsByThisSubFolder

        /// <summary>
        /// 
        /// </summary>
        public void ClearFilterEntityDefsByThisSubFolder()
        {
            this.FilterEntityDefsByThisSubFolder = string.Empty;
        }

        /// <summary>
        /// Delegate command that can execute ClearFilterEntityDefsByThisSubFolder.
        /// </summary>
        public DelegateCommand ClearFilterEntityDefsByThisSubFolderCommand
        {
            get
            {
                if (this._ClearFilterEntityDefsByThisSubFolderCommand == null) this._ClearFilterEntityDefsByThisSubFolderCommand = new DelegateCommand(ClearFilterEntityDefsByThisSubFolder);
                return _ClearFilterEntityDefsByThisSubFolderCommand;
            }
        }
        private DelegateCommand _ClearFilterEntityDefsByThisSubFolderCommand;

        #endregion


        #endregion

        #endregion

        #region AddSecurityEntityDefs

        /// <summary>
        /// Adds the security specific EntityDefs to the current domain.
        /// </summary>
        public void AddSecurityEntityDefs()
        {
            if (this._currentDomain != null)
                this._currentDomain.AddSecurityEntityDefs();
        }

        /// <summary>
        /// Delegate command that can execute AddSecurityEntityDefs.
        /// </summary>
        public DelegateCommand AddSecurityEntityDefsCommand
        {
            get
            {
                if (this._AddSecurityEntityDefsCommand == null) this._AddSecurityEntityDefsCommand = new DelegateCommand(AddSecurityEntityDefs);
                return _AddSecurityEntityDefsCommand;
            }
        }
        private DelegateCommand _AddSecurityEntityDefsCommand;

        #endregion

        #region LogIn

        /// <summary>
        /// AI Framework is a secured envirnment, and logging in is required to use the system.
        /// </summary>
        //private async void LogInUserAsync()
        //{
        //    this.IsLoggingIn = true;
        //    this.LogInMessage = "Logging in...";
        //    try
        //    {
        //        await User.LogInUserAsync("Admin", @"df#@0k0j99j$!bbvv%^b");
        //        this.IsLoggingIn = false;
        //    }
        //    catch (Exception ex)
        //    {
        //        this.LogInMessage = "Failed to log in: " + ex.Message;
        //    }
        //}

        private Task LogInUserAsync()
        {
            return Task.Factory.StartNew(async () =>
            {
                this.IsLoggingIn = true;
                this.LogInMessage = "Logging in...";
                try
                {
                    await User.LogInUserAsync("Admin", @"df#@0k0j99j$!bbvv%^b");
                    System.Diagnostics.Debug.WriteLine(User.LoggedInUser.ToString());
                    this.IsLoggingIn = false;
                    this.LoadDefaultDomain();
                }
                catch (Exception ex)
                {
                    this.LogInMessage = "Failed to log in: " + ex.Message;
                }
            });
        }

        #region IsLoggingIn

        /// <summary>
        /// Gets or sets the IsLoggingIn.
        /// </summary>
        public bool IsLoggingIn
        {
            get { return this._isLoggingIn; }
            set
            {
                if (value != this._isLoggingIn)
                {
                    this._isLoggingIn = value;
                    this.RaisePropertyChanged("IsLoggingIn");
                }
            }
        }
        private bool _isLoggingIn = false;

        #endregion

        #region LogInMessage

        /// <summary>
        /// Gets or sets the LogInMessage.
        /// </summary>
        public string LogInMessage
        {
            get { return this._LogInMessage; }
            set
            {
                if (value != this._LogInMessage)
                {
                    this._LogInMessage = value;
                    this.RaisePropertyChanged("LogInMessage");
                }
            }
        }
        private string _LogInMessage;

        #endregion

        #endregion

    }
}
